Lib = {version: 1};
Lib.apply = function (object, extend, defaults) {
    if (defaults) Lib.apply(object, defaults);
	if (object && extend && typeof extend == 'object') for (var property in extend) object[property] = extend[property];
    return object;
};
Lib.extend = function (Child, Parent) {
    Child.prototype = new Parent()
    Child.prototype.constructor = Child
    Child._super = Parent.prototype    
}
Lib.userAgent = navigator.userAgent.toLowerCase(), 
Lib.check = function (value) { return value.test(Lib.userAgent); };
Lib.is = {
	isStrict: 		document.compatMode == "CSS1Compat", 
	isOpera: 		Lib.check(/opera/),
	isChrome: 	Lib.check(/chrome/), 
	isWebKit: 	 	Lib.check(/webkit/),
	isSafari: 	 	!Lib.isChrome && Lib.check(/safari/),
	isSafari3: 	 	Lib.isSafari && Lib.check(/version\/3/),
	isSafari4: 	 	Lib.isSafari && Lib.check(/version\/4/), 
	isIE: 		 	Lib.isOpera && Lib.check(/msie/), 
	isIE7: 		 	Lib.isIE && Lib.check(/msie 7/), 
	isIE8: 		 	Lib.isIE && Lib.check(/msie 8/),
	isGecko: 		!Lib.isWebKit && Lib.check(/gecko/),
	isGecko3: 	Lib.isGecko && Lib.check(/rv:1\.9/),
	isBorderBox: Lib.isIE && !Lib.isStrict,
	
	isWindows: 	Lib.check(/windows|win32/),
	isMac: 		 	Lib.check(/macintosh|mac os x/),
	isAir: 		 	Lib.check(/adobeair/),
	isLinux:			Lib.check(/linux/),
	
	isSecure: 	 	/^https/i.test(window.location.protocol),
	
	isEmpty: 		function (value, allowBlank){ return value === null || value === undefined || ((Lib.isArray(value) && !value.length)) || (!allowBlank ? value === '' : false); },
	isArray: 		function (value){ return Object.prototype.toString.apply(value) === '[object Array]'; },
	isObject: 		function (value){ return value && typeof value == "object"; },
	isPrimitive: 	function (value){ var tmp = typeof value; return tmp == 'string' || tmp == 'number' || tmp == 'boolean'; },
	isFunction: 	function (value) { return typeof value == "function"; }
};
Lib.apply(Lib, Lib.is);
Lib.applyIf = function (object, extend) {
	if (object){
		for (var property in extend){
			if (Lib.isEmpty(object[property])){
				object[property] = extend[property];
			}
		}
	}
	return object;
}
Lib.getXMLHTTPRequest = function (){
	try {
		try { return new XMLHttpRequest();}
			catch (e){}
		try { return new ActiveXObject('Msxml2.XMLHTTP');} 
			catch (e){}
		try { return new ActiveXObject('Microsoft.XMLHTTP');} 
			catch (e){}
	} catch (e){}
}
Lib.getFileHeaders = function (url){
	var request = Lib.getXMLHTTPRequest();
	if (!request) return false;
	request.open('HEAD', url, false);
	request.send(null);
	if (request.getAllResponseHeaders && request.status != '404') return request.getAllResponseHeaders();
};

//############################################################################################################
// Preloader
// version: 1
//############################################################################################################

Preloader = function (options){
	var self = this;
	this.options = {};
	this.defaults = {
		append: $('body'),
		cls: 'preloader',
		load: []
	};
	Lib.apply(this.options, options, this.defaults);
	
	this.controller = new Preloader.LoadingController();
	this.controller.bind('change', function (controller, procces, request){
		self.setValue(((controller.session.length-controller.order.length+1)/controller.session.length*100)+'%');
	});
	this.element = false;
	this.create();
}

Preloader.prototype = {
	create:  function (){
		var self = this;
		if (!!this.element) return false;
		
		this.parent 	= this.options.append;
		this.element 	= $('<div class="'+this.options.cls+'"/>').appendTo(this.parent);
		
		this.waraperElement 		= $('<div class="preloader-wraper"/>').appendTo(this.element);
		this.innerElement 			= $('<div class="preloader-inner"/>').appendTo(this.waraperElement);
		this.barElement				= $('<div class="preloader-progress"/>').appendTo(this.innerElement);
		this.barValueElement		= $('<div class="preloader-value"/>').appendTo(this.barElement);
		this.textShadowElement = $('<div class="preloader-text-shadow"/>').appendTo(this.innerElement);
		this.textElement	 		= $('<div class="preloader-text"/>').appendTo(this.innerElement);
		this.closeElement	 		= $('<div class="preloader-close"><span>x</span></div>').appendTo(this.innerElement);
		this.closeElement.click(function (){
			self.controller.stop();
			self.destroy();
		});
	},
	destroy: function (){
		var self = this;
		this.hide("slow", function (){
			self.controller.stop();
			self.element.remove();
		});
	},
	show: function (fx, params, special){
		this.element.show(fx || 100, params);
	},
	hide: function (fx, params){
		this.element.hide(fx || "slow", params);
	},
	
	setText: function (value){
		this.text = value;
		this.textElement.text(value);
		this.textShadowElement.text(value);
	},
	getText: function (){
		return this.text;
	},
	setValue: function (value){
		this.value = value;
		this.barValueElement.width(this.value);
	},
	getValue: function (){
		return this.value;
	},
	
	
	start: function (){
		this.controller.start();
	},
	pause: function (time){
		this.controller.pause(time);
	},
	stop: function (){
		this.controller.stop();
	},
	add: function (object){
		this.controller.add(object);
	}
};

/****************************************************************
* Process
****************************************************************/
Preloader.Process = function (options){
	var self = this;
	
	this.options = {};
	this.reguest = null;
	this.defaults = {
		callback: [],
		success: [],
		error: [],
		handler: [],
		path: '',
		method: 'GET',
		data: null
	};
	Lib.apply(this.options, options, this.defaults);
	
	var events = {
		callback: 	typeof this.options.callback 	== 'array' ? this.options.callback : [this.options.callback],
		success:  	typeof this.options.success 	== 'array' ? this.options.success : [this.options.success],
		error:  		typeof this.options.error 			== 'array' ? this.options.error 		: [this.options.error],
		handler: 	typeof this.options.handler 		== 'array' ? this.options.handler 	: [this.options.handler]
	}; 
	
	function localPath(url){
		return url;
		if (url == '/') return window.location.href.replace(window.location.pathname, '') + '/';
		function parsePath(url){
			var path = {};
			var pattern = "^(([^:/\\?#]+):)?(//(([^:/\\?#]*)(?::([^/\\?#]*))?))?([^\\?#]*)(\\?([^#]*))?(#(.*))?$";
			var rx = new RegExp(pattern);
			var parts = rx.exec(url);
			
			path.href 			= parts[0] || "";
			path.protocol 	= parts[1] || "";
			path.host 			= parts[4] || "";
			path.hostname 	= parts[5] || "";
			path.port 			= parts[6] || "";
			path.pathname 	= parts[7] || "/";
			path.search 		= parts[8] || "";
			path.hash 			= parts[10] || "";
			
			return path;
		}
		var local 	= parsePath(window.location.href);
		var self   	= parsePath(url);
		var path 	= url;
		
		if (!self.host && !self.protocol){
			var name = self.href.replace(/^([\.]+[\/])/, '');
			path = local.protocol+'//'+local.host+'/'+(name);
		}
		return path;
	}
	
	function parseHeaders(headers){
		var object = {};
		var array = headers.split('\n');
		
		for (var i in array){
			var name = array[i].match(/^[\w\d\-]+/g);
			if (name) object[name] = array[i].replace(/^[\w\d\-]+\:/g, '');
		}
		
		return object;
	}
	this.path 		= localPath(this.options.path);
	var headers 	= Lib.getFileHeaders(this.path);
	this.size 		= headers ? headers['Content-Length'] : false;
	
	this.events = {
		success: 	this.options.success 	? (typeof this.options.success 	== 'array' ? this.options.success: 	[this.options.success]) 	: [],
		error: 		this.options.error 		? (typeof this.options.error 		== 'array' ? this.options.error: 		[this.options.error]) 		: [],
		callback: 	this.options.callback 	? (typeof this.options.callback 	== 'array' ? this.options.callback: 	[this.options.callback]) 	: [],
		handler: 	this.options.handler 	? (typeof this.options.handler 	== 'array' ? this.options.handler: 	[this.options.handler]) 	: [],
	};
	
	this.start = function (){
		this.load();
	};
	this.pause = function (time){
		this.stop();
		if (time) {
			setTimeout(function (){
				self.unpause();
			}, time);
		}
	};
	this.unpause = function (){
		this.load();
	};
	this.stop = function (){
		try {
			this.request.abort();
			return true;
		} catch (e){}
		return false;
	};
	this.load = function (){
		this.request = Lib.getXMLHTTPRequest();
		this.request.onreadystatechange = function (){
			self.trigger('handler');
			
			if (self.request.readyState == 4 && self.request.status == 200) 
				self.trigger('success');
			if (self.request.readyState == 4 && self.request.status != 200) 	
				self.trigger('error');
			if (self.request.readyState == 4) 												
				self.trigger('callback');
		}
		
		this.request.open(this.options.method, this.path, true);
		this.request.send(this.options.data);
	};
	
	this.bind = function(type, fn){
		events[type].push(fn);
	};
	this.unbind = function (type){
		events[type] = [];
	};
	this.trigger = function (type){
		if (!events[type]) return;
		for( var e in events[type]){
			if (typeof events[type][e] == 'function'){
				events[type][e](arguments[1], arguments[2], arguments[3], arguments[4]);
			}
		}
	};
}

/****************************************************************
* LoadingController
****************************************************************/
Preloader.LoadingController = function (){
	var self = this;
	
	this.order = [];
	this.session = [];
	this.paused = false;
	
	var events = {
		change: [],
		stop: [],
		start: [],
		pause: [],
		unpause: [],
	};
	
	this.start = function (){
		this._continue();
	};
	this._continue = function (){
		if (!this.order[0]){ 
			this.session = [];
			return false;
		}
		this.order[0].start();
		this.order[0].bind('callback', function (){
			self.order.shift();
			self._continue();
		});
	};
	this.stop = function (){
		if (this.order[0]) this.order[0].stop();
		this.order = [];
	};
	this.pause = function (time){
		if (this.order[0]){
			this.order[0].pause(time);
			this.paused = true;
		}
	};
	this.unpause = function (){
		if (this.order[0]){
			this.order[0].unpause();
			this.paused = false;
		}
	};
	this.add = function (object){
		var array = object;
		if (typeof object != "array") array = [object];
		
		for (var item in array){
			var process = new Preloader.Process(array[item]);
			process.bind('handler', function (object){
				self.trigger('change', self, process, process.request);
			});
			this.order.push(process);
			this.session.push(process);
		}
	};
	
	this.bind = function(type, fn){
		events[type].push(fn);
	};
	this.unbind = function (type){
		events[type] = [];
	};
	this.trigger = function (type){
		if (!events[type]) return;
		for( var e in events[type]){
			if (typeof events[type][e] == 'function'){
				events[type][e](arguments[1], arguments[2], arguments[3], arguments[4]);
			}
		}
	};
};